﻿namespace ContactsSync.Providers.Outlook.Providers
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Drawing;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using ContactsSync.Infrastructure;
    using ContactsSync.Infrastructure.Models;
    using Microsoft.Office.Interop.Outlook;
    using Microsoft.Practices.Composite.Events;
    using Models;
    using Exception = System.Exception;

    public class OutlookContactsProvider : IOutlookContactsProvider
    {
        private readonly IImageDownloader imageDownloader;

        public OutlookContactsProvider(IImageDownloader imageDownloader)
        {
            this.imageDownloader = imageDownloader;
            this.imageDownloader.DownloadImageCompleted += this.ImageDownloader_DownloadImageCompleted;
        }

        public event EventHandler<DataEventArgs<IList<Contact>>> RetrieveContactsCompleted;

        public event EventHandler<DataEventArgs<bool>> SaveContactCompleted;

        public string ProviderName
        {
            get { return "Outlook"; }
        }

        public void RetrieveContactsAsync()
        {
            this.RetrieveContactsAsync(null);
        }

        public void RetrieveContactsAsync(ContactFolder folder)
        {
            BackgroundWorker backgroundWorker = new BackgroundWorker();
            backgroundWorker.RunWorkerCompleted += this.BackgroundWorker_RunWorkerCompleted;
            backgroundWorker.DoWork += BackgroundWorker_DoWork;
            backgroundWorker.RunWorkerAsync(folder);
        }

        public void SaveContactAsync(Contact contact)
        {
            this.imageDownloader.DownloadImageAsync(contact);
        }

        public IList<ContactFolder> RetrieveContactFolders()
        {
            IList<ContactFolder> folders = new List<ContactFolder>();

            Application application = null;
            try
            {
                application = new ApplicationClass();

                NameSpace session = application
                    .ActiveExplorer()
                    .Session;
                
                IEnumerable<MAPIFolder> contactFolders = GetAllContactFolders(session.Folders);

                foreach (MAPIFolder folder in contactFolders)
                {
                    ContactFolder contactFolder = new ContactFolder(folder.EntryID, folder.FolderPath);

                    folders.Add(contactFolder);
                }
            }
            catch (Exception)
            {
                if (application != null)
                {
                    application.Quit();
                }
            }

            return folders;
        }

        [CLSCompliant(false)]
        private static Image GetContactPicture(_ContactItem contact)
        {
            if (contact.HasPicture)
            {
                foreach (Attachment att in contact.Attachments)
                {
                    if (att.DisplayName == "ContactPicture.jpg")
                    {
                        try
                        {
                            string picturePath = Path.GetDirectoryName(Path.GetTempPath()) + "\\Contact_" + contact.EntryID + ".jpg";
                            att.SaveAsFile(picturePath);

                            return new Bitmap(picturePath);
                        }
                        catch
                        {
                            return null;
                        }
                    }
                }
            }

            return null;
        }

        private static IEnumerable<MAPIFolder> GetAllContactFolders(Folders folders)
        {
            foreach (MAPIFolder folder in folders)
            {
                foreach (MAPIFolder subFolder in folder.Folders)
                {
                    if (subFolder.DefaultItemType == OlItemType.olContactItem)
                    {
                        yield return subFolder;
                    }
                }
            }
        }

        private static void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<Contact> contacts = new List<Contact>();

            try
            {
                Application application = new ApplicationClass();

                NameSpace session = application
                    .ActiveExplorer()
                    .Session;

                IEnumerable<MAPIFolder> contactFolders = GetAllContactFolders(session.Folders);

                ContactFolder contactFolder = e.Argument as ContactFolder;

                if (contactFolder != null)
                {
                    contactFolders = contactFolders.Where(x => x.EntryID == contactFolder.Id);
                }

                foreach (ContactItem foundContact in contactFolders.SelectMany(f => f.Items.OfType<ContactItem>()))
                {
                    if (!string.IsNullOrEmpty(foundContact.FirstName) || !string.IsNullOrEmpty(foundContact.Email1Address))
                    {
                        Contact contact = new Contact();
                        contact.Id = foundContact.EntryID;

                        contact.FirstName = string.IsNullOrEmpty(foundContact.FirstName) ? foundContact.Email1Address : foundContact.FirstName;
                        contact.LastName = foundContact.LastName;
                        contact.ImageSource = GetContactPicture(foundContact);

                        // contact.CompleteName = ((Microsoft.Office.Interop.Outlook.ContactItem)foundContact).FullName;
                        contacts.Add(contact);
                    }
                }

                contacts.Sort((c1, c2) => string.Compare(c1.FirstName, c2.FirstName, StringComparison.OrdinalIgnoreCase));
            }
            catch
            {
            }

            e.Result = contacts;
        }

        private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            IList<Contact> contacts = e.Result as IList<Contact>;

            if (contacts == null)
            {
                contacts = new List<Contact>();
            }

            this.OnRetrieveContactsCompleted(contacts);
        }

        private void ImageDownloader_DownloadImageCompleted(object sender, DataEventArgs<ContactImage> e)
        {
            bool result = false;
            if (e.Value != null)
            {
                Application application = new ApplicationClass();

                ContactItem outlookContact =
                    application
                        .ActiveExplorer()
                        .Session.GetItemFromID(e.Value.Id, null) as ContactItem;

                if (outlookContact != null)
                {
                    try
                    {
                        string tempPath = Path.GetTempPath();
                        Image image = Image.FromStream(e.Value.Stream);

                        string fileName = String.Format(CultureInfo.InvariantCulture, "ContactsSync_Contact{0}.{1}", e.Value.Id, ".jpg");
                        string picturePath = Path.Combine(tempPath, fileName);

                        image.Save(picturePath, image.RawFormat);

                        outlookContact.AddPicture(picturePath);
                        outlookContact.Save();

                        e.Value.Stream.Close();

                        result = true;
                    }
                    catch
                    {
                        result = false;
                    }
                }
            }

            this.OnSaveContactCompleted(result);
        }

        private void OnRetrieveContactsCompleted(IList<Contact> contacts)
        {
            EventHandler<DataEventArgs<IList<Contact>>> completed = this.RetrieveContactsCompleted;
            if (completed != null)
            {
                completed(this, new DataEventArgs<IList<Contact>>(contacts));
            }
        }

        private void OnSaveContactCompleted(bool value)
        {
            EventHandler<DataEventArgs<bool>> completed = this.SaveContactCompleted;
            if (completed != null)
            {
                completed(this, new DataEventArgs<bool>(value));
            }
        }
    }
}